/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.impl;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Laurent WOUTERS
 */
public class DefaultStaticEvaluator extends StaticEvaluator {
    protected xowl.interpreter.Repository repository;
    protected List<xowl.lang.runtime.Class> classUnions;
    protected List<xowl.lang.runtime.Class> classIntersections;
    protected List<xowl.lang.runtime.Class> classOneOfs;
    protected List<xowl.lang.runtime.Class> classComplements;
    protected List<xowl.lang.runtime.ObjectProperty> propInverses;
    protected List<xowl.lang.runtime.Datatype> dataUnions;
    protected List<xowl.lang.runtime.Datatype> dataIntersections;
    protected List<xowl.lang.runtime.Datatype> dataOneOfs;
    protected List<xowl.lang.runtime.Datatype> dataComplements;
    protected List<xowl.lang.owl2.AnonymousIndividual> anonymousIndividuals;

    public DefaultStaticEvaluator(xowl.interpreter.Repository repository) {
        this.repository = repository;
        this.classUnions = new ArrayList<xowl.lang.runtime.Class>();
        this.classIntersections = new ArrayList<xowl.lang.runtime.Class>();
        this.classOneOfs = new ArrayList<xowl.lang.runtime.Class>();
        this.classComplements = new ArrayList<xowl.lang.runtime.Class>();
        this.propInverses = new ArrayList<xowl.lang.runtime.ObjectProperty>();
        this.dataUnions = new ArrayList<xowl.lang.runtime.Datatype>();
        this.dataIntersections = new ArrayList<xowl.lang.runtime.Datatype>();
        this.dataOneOfs = new ArrayList<xowl.lang.runtime.Datatype>();
        this.dataComplements = new ArrayList<xowl.lang.runtime.Datatype>();
        this.anonymousIndividuals = new ArrayList<xowl.lang.owl2.AnonymousIndividual>();
    }

    @Override protected xowl.lang.runtime.Entity eval_Entity(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.resolveEntity(expression);
    }

    // <editor-fold defaultstate="collapsed" desc="parent.evalClass">
    @Override protected xowl.lang.runtime.Class eval_Class(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsClass(repository.resolveEntity(expression));
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectUnionOf(Evaluator parent, xowl.lang.owl2.ObjectUnionOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Class> unified = new ArrayList<xowl.lang.runtime.Class>();
        for (xowl.lang.runtime.Class exp : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getClassSeq()))
            unified.add(exp);
        // Try to find previously resolved class
        for (xowl.lang.runtime.Class previous : classUnions) {
            if (previous.getAllClassUnionOf().size() != unified.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Class c : previous.getAllClassUnionOf()) {
                if (!unified.contains(c)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        // New union
        xowl.lang.runtime.Class union = new xowl.lang.runtime.Class();
        for (xowl.lang.runtime.Class c : unified)
            union.addClassUnionOf(c);
        classUnions.add(union);
        return union;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectIntersectionOf(Evaluator parent, xowl.lang.owl2.ObjectIntersectionOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Class> intersected = new ArrayList<xowl.lang.runtime.Class>();
        for (xowl.lang.runtime.Class exp : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getClassSeq()))
            intersected.add(exp);
        // Try to find previously resolved class
        for (xowl.lang.runtime.Class previous : classIntersections) {
            if (previous.getAllClassIntersectionOf().size() != intersected.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Class c : previous.getAllClassIntersectionOf()) {
                if (!intersected.contains(c)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        // New union
        xowl.lang.runtime.Class intersection = new xowl.lang.runtime.Class();
        for (xowl.lang.runtime.Class c : intersected)
            intersection.addClassUnionOf(c);
        classIntersections.add(intersection);
        return intersection;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectOneOf(Evaluator parent, xowl.lang.owl2.ObjectOneOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Individual> individuals = new ArrayList<xowl.lang.runtime.Individual>();
        for (xowl.lang.runtime.Individual exp : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getIndividualSeq()))
            individuals.add(exp);
        // Try to find previously resolved class
        for (xowl.lang.runtime.Class previous : classOneOfs) {
            if (previous.getAllClassOneOf().size() != individuals.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Individual i : previous.getAllClassOneOf()) {
                if (!individuals.contains(i)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        xowl.lang.runtime.Class oneOf = new xowl.lang.runtime.Class();
        for (xowl.lang.runtime.Individual i : individuals)
            oneOf.addClassOneOf(i);
        classOneOfs.add(oneOf);
        return oneOf;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectComplementOf(Evaluator parent, xowl.lang.owl2.ObjectComplementOf expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class complement = parent.evalClass(parent, expression.getClasse());
        if (classComplements.contains(complement))
            return complement.getClassComplementOf();
        xowl.lang.runtime.Class complementOf = new xowl.lang.runtime.Class();
        complementOf.setClassComplementOf(complement);
        classComplements.add(complement);
        classComplements.add(complementOf);
        return complementOf;
    }
    @Override protected xowl.lang.runtime.Class eval_DataAllValuesFrom(Evaluator parent, xowl.lang.owl2.DataAllValuesFrom expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataAllValuesFrom restriction = new xowl.lang.runtime.DataAllValuesFrom();
        for (xowl.lang.runtime.DataProperty prop : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getDataPropertySeq()))
            restriction.addDataProperties(prop);
        restriction.setDatatype(parent.evalDatatype(parent, expression.getDatarange()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_DataExactCardinality(Evaluator parent, xowl.lang.owl2.DataExactCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataExactCardinality restriction = new xowl.lang.runtime.DataExactCardinality();
        restriction.setDataProperty(parent.evalDataProp(parent, expression.getDataProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getDatarange() != null)
            restriction.setDatatype(parent.evalDatatype(parent, expression.getDatarange()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_DataHasValue(Evaluator parent, xowl.lang.owl2.DataHasValue expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataHasValue restriction = new xowl.lang.runtime.DataHasValue();
        restriction.setDataProperty(parent.evalDataProp(parent, expression.getDataProperty()));
        restriction.setLiteral(parent.evalLiteral(parent, expression.getLiteral()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_DataMaxCardinality(Evaluator parent, xowl.lang.owl2.DataMaxCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataMaxCardinality restriction = new xowl.lang.runtime.DataMaxCardinality();
        restriction.setDataProperty(parent.evalDataProp(parent, expression.getDataProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getDatarange() != null)
            restriction.setDatatype(parent.evalDatatype(parent, expression.getDatarange()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_DataMinCardinality(Evaluator parent, xowl.lang.owl2.DataMinCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataMinCardinality restriction = new xowl.lang.runtime.DataMinCardinality();
        restriction.setDataProperty(parent.evalDataProp(parent, expression.getDataProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getDatarange() != null)
            restriction.setDatatype(parent.evalDatatype(parent, expression.getDatarange()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_DataSomeValuesFrom(Evaluator parent, xowl.lang.owl2.DataSomeValuesFrom expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.DataSomeValuesFrom restriction = new xowl.lang.runtime.DataSomeValuesFrom();
        for (xowl.lang.runtime.DataProperty prop : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getDataPropertySeq()))
            restriction.addDataProperties(prop);
        restriction.setDatatype(parent.evalDatatype(parent, expression.getDatarange()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectAllValuesFrom(Evaluator parent, xowl.lang.owl2.ObjectAllValuesFrom expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectAllValuesFrom restriction = new xowl.lang.runtime.ObjectAllValuesFrom();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setClasse(parent.evalClass(parent, expression.getClasse()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectExactCardinality(Evaluator parent, xowl.lang.owl2.ObjectExactCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectExactCardinality restriction = new xowl.lang.runtime.ObjectExactCardinality();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getClasse() != null)
            restriction.setClasse(parent.evalClass(parent, expression.getClasse()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectHasSelf(Evaluator parent, xowl.lang.owl2.ObjectHasSelf expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectHasSelf restriction = new xowl.lang.runtime.ObjectHasSelf();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectHasValue(Evaluator parent, xowl.lang.owl2.ObjectHasValue expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectHasValue restriction = new xowl.lang.runtime.ObjectHasValue();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setIndividual(parent.evalIndividual(parent, expression.getIndividual()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectMaxCardinality(Evaluator parent, xowl.lang.owl2.ObjectMaxCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectMaxCardinality restriction = new xowl.lang.runtime.ObjectMaxCardinality();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getClasse() != null)
            restriction.setClasse(parent.evalClass(parent, expression.getClasse()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectMinCardinality(Evaluator parent, xowl.lang.owl2.ObjectMinCardinality expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectMinCardinality restriction = new xowl.lang.runtime.ObjectMinCardinality();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setCardinality(Integer.parseInt(parent.evalLiteral(parent, expression.getCardinality()).getLexicalValue()));
        if (expression.getClasse() != null)
            restriction.setClasse(parent.evalClass(parent, expression.getClasse()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    @Override protected xowl.lang.runtime.Class eval_ObjectSomeValuesFrom(Evaluator parent, xowl.lang.owl2.ObjectSomeValuesFrom expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Class classe = new xowl.lang.runtime.Class();
        xowl.lang.runtime.ObjectSomeValuesFrom restriction = new xowl.lang.runtime.ObjectSomeValuesFrom();
        restriction.setObjectProperty(parent.evalObjProp(parent, expression.getObjectProperty()));
        restriction.setClasse(parent.evalClass(parent, expression.getClasse()));
        classe.addClassRestrictions(restriction);
        return classe;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalObjProp">
    @Override protected xowl.lang.runtime.ObjectProperty eval_ObjectProperty(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsObjectProperty(repository.resolveEntity(expression));
    }
    @Override protected xowl.lang.runtime.ObjectProperty eval_ObjectInverseOf(Evaluator parent, xowl.lang.owl2.ObjectInverseOf expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.ObjectProperty inverse = parent.evalObjProp(parent, expression.getInverse());
        if (propInverses.contains(inverse))
            return inverse.getInverseOf();
        xowl.lang.runtime.ObjectProperty inverseOf = new xowl.lang.runtime.ObjectProperty();
        inverseOf.setInverseOf(inverse);
        propInverses.add(inverse);
        propInverses.add(inverseOf);
        return inverseOf;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalDataProp">
    @Override protected xowl.lang.runtime.DataProperty eval_DataProperty(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsDataProperty(repository.resolveEntity(expression));
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalDatatype">
    @Override protected xowl.lang.runtime.Datatype eval_Datatype(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsDatatype(repository.resolveEntity(expression));
    }
    @Override protected xowl.lang.runtime.Datatype eval_DataComplementOf(Evaluator parent, xowl.lang.owl2.DataComplementOf expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Datatype complement = parent.evalDatatype(parent, expression.getDatarange());
        if (dataComplements.contains(complement))
            return complement.getDataComplementOf();
        xowl.lang.runtime.Datatype complementOf = new xowl.lang.runtime.Datatype();
        complementOf.setDataComplementOf(complement);
        dataComplements.add(complement);
        dataComplements.add(complementOf);
        return complementOf;
    }
    @Override protected xowl.lang.runtime.Datatype eval_DataIntersectionOf(Evaluator parent, xowl.lang.owl2.DataIntersectionOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Datatype> intersected = new ArrayList<xowl.lang.runtime.Datatype>();
        for (xowl.lang.runtime.Datatype exp : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getDatarangeSeq()))
            intersected.add(exp);
        // Try to find previously resolved datatype
        for (xowl.lang.runtime.Datatype previous : dataIntersections) {
            if (previous.getAllDataIntersectionOf().size() != intersected.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Datatype c : previous.getAllDataIntersectionOf()) {
                if (!intersected.contains(c)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        // New union
        xowl.lang.runtime.Datatype intersection = new xowl.lang.runtime.Datatype();
        for (xowl.lang.runtime.Datatype c : intersected)
            intersection.addDataIntersectionOf(c);
        dataIntersections.add(intersection);
        return intersection;
    }
    @Override protected xowl.lang.runtime.Datatype eval_DataOneOf(Evaluator parent, xowl.lang.owl2.DataOneOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Literal> literals = xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getLiteralSeq());
        // Try to find previously resolved class
        for (xowl.lang.runtime.Datatype previous : dataOneOfs) {
            if (previous.getAllDataOneOf().size() != literals.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Literal i : previous.getAllDataOneOf()) {
                if (!literals.contains(i)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        xowl.lang.runtime.Datatype oneOf = new xowl.lang.runtime.Datatype();
        for (xowl.lang.runtime.Literal i : literals)
            oneOf.addDataOneOf(i);
        dataOneOfs.add(oneOf);
        return oneOf;
    }
    @Override protected xowl.lang.runtime.Datatype eval_DatatypeRestriction(Evaluator parent, xowl.lang.owl2.DatatypeRestriction expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Datatype datatype = new xowl.lang.runtime.Datatype();
        for (xowl.lang.owl2.FacetRestriction restriction : expression.getAllFacetRestrictions()) {
            xowl.lang.runtime.DatatypeRestriction restric = new xowl.lang.runtime.DatatypeRestriction();
            restric.setFacet(restriction.getConstrainingFacet());
            restric.setValueLiteral(parent.evalLiteral(parent, restriction.getConstrainingValue()));
            datatype.addDataRestrictions(restric);
        }
        return datatype;
    }
    @Override protected xowl.lang.runtime.Datatype eval_DataUnionOf(Evaluator parent, xowl.lang.owl2.DataUnionOf expression) throws xowl.interpreter.XOWLException {
        List<xowl.lang.runtime.Datatype> unified = new ArrayList<xowl.lang.runtime.Datatype>();
        for (xowl.lang.runtime.Datatype exp : xowl.interpreter.impl.Sequences.toEvaluatedList(parent, expression.getDatarangeSeq()))
            unified.add(exp);
        // Try to find previously resolved datatype
        for (xowl.lang.runtime.Datatype previous : dataUnions) {
            if (previous.getAllDataUnionOf().size() != unified.size())
                continue;
            boolean equal = true;
            for (xowl.lang.runtime.Datatype c : previous.getAllDataUnionOf()) {
                if (!unified.contains(c)) {
                    equal = false;
                    break;
                }
            }
            if (equal)
                return previous;
        }
        // New union
        xowl.lang.runtime.Datatype union = new xowl.lang.runtime.Datatype();
        for (xowl.lang.runtime.Datatype c : unified)
            union.addDataUnionOf(c);
        dataUnions.add(union);
        return union;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalIndividual">
    @Override protected xowl.lang.runtime.Individual eval_NamedIndividual(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsIndividual(repository.resolveEntity(expression));
    }
    @Override protected xowl.lang.runtime.Individual eval_AnonymousIndividual(Evaluator parent, xowl.lang.owl2.AnonymousIndividual expression) throws xowl.interpreter.XOWLException {
        anonymousIndividuals.add(expression);
        return expression;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalLiteral">
    @Override protected xowl.lang.runtime.Literal eval_Literal(Evaluator parent, xowl.lang.owl2.Literal expression) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Literal literal = new xowl.lang.runtime.Literal();
        literal.setLexicalValue(expression.getLexicalValue());
        literal.setMemberOf(repository.interpretAsDatatype(repository.resolveEntity(expression.getMemberOf())));
        return literal;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="parent.evalFunction">
    @Override protected xowl.lang.runtime.Function eval_Function(Evaluator parent, xowl.lang.owl2.IRI expression) throws xowl.interpreter.XOWLException {
        return repository.interpretAsFunction(repository.resolveEntity(expression));
    }
    // </editor-fold>
}
